package org.example.company.config;

import org.example.company.filter.JwtTokenAuthenticationFailter;
import org.example.company.handler.AuthenticationFailHandler;
import org.example.company.handler.AuthenticationSuceessHandler;
import org.example.company.handler.MyLogoutSuccessHandler;
import org.example.company.service.SecurityUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UserDetailsRepositoryReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.context.NoOpServerSecurityContextRepository;
import reactor.core.publisher.Mono;

@EnableWebFluxSecurity
public class SecurityConfig {

    private static final String[] excludeAuthPages={"/auth/login", "/auth/register"};

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


    //定义了一个响应式的认证管理器
    //用于处理基于用户名和密码的身份认证。在认证过程中，系统会从 ReactiveUserDetailsService 中加载用户信息，并使用 PasswordEncoder 来验证密码的正确性
    @Bean
    public ReactiveAuthenticationManager reactiveAuthenticationManager(SecurityUserDetailService reactiveUserDetailsService, PasswordEncoder passwordEncoder){
        UserDetailsRepositoryReactiveAuthenticationManager authenticationManager = new UserDetailsRepositoryReactiveAuthenticationManager(reactiveUserDetailsService);
        authenticationManager.setPasswordEncoder(passwordEncoder);
        return authenticationManager;
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http,
                                                         JwtProperties properties,
                                                         AuthenticationSuceessHandler authenticationSuceessHandler,
                                                         AuthenticationFailHandler authenticationFailHandler,
                                                         MyLogoutSuccessHandler myLogoutSuccessHandler,
                                                         ReactiveAuthenticationManager manager){
        //禁用 CSRF 保护。对于很多 RESTful API，这一保护机制通常是关闭的，因为 API 通常通过 Token 来验证用户身份。
         return http.csrf(ServerHttpSecurity.CsrfSpec::disable)
                //禁用 HTTP Basic 认证，这里选择使用自定义认证机制而不是默认的 HTTP Basic。
                .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable)
                //设置自定义的认证管理器，用于处理身份认证请求。这个 manager 是通过之前定义的 ReactiveAuthenticationManager 实例注入的。
                .authenticationManager(manager)
                //配置异常处理器，用于处理认证和授权过程中的异常。
                .exceptionHandling()
                //设置认证入口点（authenticationEntryPoint），在认证失败时触发。这里设置为 null，可能是为了在其他地方设置自定义的入口点。
                .authenticationEntryPoint(null)
                //设置访问拒绝处理器。当用户尝试访问没有权限的资源时，这个处理器会被触发。
                .accessDeniedHandler((swe,e) -> {
                    swe.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
                    return swe.getResponse().writeWith(Mono.just(new DefaultDataBufferFactory().wrap("FORBIDDEN".getBytes())));
                })
                .and()
                //设置安全上下文存储库。NoOpServerSecurityContextRepository 表示不使用任何安全上下文存储。
                // 这通常用于无状态的安全配置（例如 Token 认证），不会将用户的安全信息存储在会话中。
                .securityContextRepository(NoOpServerSecurityContextRepository.getInstance())
                //配置认证和授权规则，以下配置用于指定不同路径的访问控制规则。
                .authorizeExchange()
                //对所有 HTTP OPTIONS 请求允许访问，无需认证。OPTIONS 方法通常用于跨域预检请求。
                .pathMatchers(excludeAuthPages).permitAll()
                //对其他所有请求都要求认证。即，除了前面明确允许访问的路径，其他路径都需要经过认证。
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .anyExchange()
                .authenticated()
                .and()
                .formLogin().loginPage("/auth/login")
                //自定义认证成功处理器
                .authenticationSuccessHandler(authenticationSuceessHandler)
                //自定义认证失败处理器
                .authenticationFailureHandler(authenticationFailHandler)
                .and()
                //配置登出功能，设置登出 URL 为 /auth/logout。
                .logout().logoutUrl("/auth/logout")
                //logoutHandler(null)
                .logoutHandler(myLogoutSuccessHandler)
                .and()
                //添加自定义过滤器。
                .addFilterAt(new JwtTokenAuthenticationFailter(properties), SecurityWebFiltersOrder.HTTP_BASIC)
                .build();
    }



}
